package baidu

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gitee.com/sourcandy/starter/encrypt"
	"github.com/tidwall/gjson"
	"io"
	"net/url"
	"strconv"
	"strings"
)

const (
	BaiDuPanApi       = "http://pan.baidu.com/"
	BaiDuOpenApi      = "https://openapi.baidu.com/"
	BaiDuPcsApi       = "https://d.pcs.baidu.com/"
	blockMaxSize      = 4 * 1024 * 1024
	CategoryTypeVideo = 1 //视频
	CategoryTypeAudio = 2 //音频
	CategoryTypePic   = 3 //图片
	CategoryTypeFile  = 4 //文档
	CategoryTypeApply = 5 //应用
	CategoryTypeOther = 6 //其他
	CategoryTypeSeed  = 7 //种子
)

type Bcloud struct {
	AppKey        string
	AppSecret     string
	UserCode      string
	DeviceCode    string
	AccessToken   string
	RefreshTokens string
	AppName       string
	FileStream    []byte
	FileList      []io.Reader
	FileName      string
	Block         []string
}

// GetCode 获取授权code
func (b *Bcloud) GetCode() (string, error) {
	urls := BaiDuOpenApi + "oauth/2.0/device/code?response_type=device_code&client_id=" +
		b.AppKey + "&scope=basic,netdisk"
	list, err := HttpGets(urls)
	if err != nil {
		return "", err
	}
	device_code := gjson.Get(list, "device_code").String()
	user_code := gjson.Get(list, "user_code").String()
	url := gjson.Get(list, "verification_url").String()
	b.DeviceCode = device_code
	b.UserCode = user_code
	return url, err

}

// Upload 上传
func (b *Bcloud) Upload() (string, error) {
	info, err := b.PreUpload()
	if err != nil {
		return "", err
	}
	if errno := gjson.Get(info, "errno").Int(); errno != 0 {
		return "", fmt.Errorf("预上传错误%s", gjson.Get(info, "error_msg").String())
	}
	id := gjson.Get(info, "uploadid").String()
	err = b.PianUpload(id)
	if err != nil {
		return "", err
	}
	infoss, err := b.Create(id)
	if err != nil {
		return "", err
	}
	return infoss, nil
}

//// SliceUpload 小于4M一次性上传
//func (b *Bcloud) SliceUpload() (string, error) {
//	urls := BaiDuPcsApi + "rest/2.0/pcs/file?method=upload&access_token=" +
//		b.AccessToken + "&path=/apps/" + b.AppName + "/" + b.FileName
//	info, err := HttpFile(urls, b.FileName, b.File)
//	return info, err
//
//}

// Download 下载
func (b *Bcloud) Download(fsids []uint64) (string, error) {
	info, err := b.GetFileInfo(fsids)
	if err != nil {
		return "", err
	}
	if val := gjson.Get(info, "errno").Int(); val != 0 {
		return "", fmt.Errorf("查询信息失败%s", gjson.Get(info, "errmsg").String())
	}
	list := gjson.Get(info, "list").Array()
	url := list[0].Get("dlink").String()
	url = url + "&access_token=" + b.AccessToken
	files, err := HttpPost(url, map[string]string{})
	if err != nil {
		return "", err
	}
	return files, nil
}

// GetFileInfo 获取文件信息
func (b *Bcloud) GetFileInfo(fsids []uint64) (string, error) {
	fsidJs, err := json.Marshal(fsids)
	if err != nil {
		return "", err
	}
	sids := url.QueryEscape(string(fsidJs))
	path := ""
	for i, s := range strings.Split(b.FileName, "/") {
		if i == len(strings.Split(b.FileName, "/"))-1 {
			break
		}
		path = path + "/" + s
	}
	urls := BaiDuPanApi + "rest/2.0/xpan/multimedia?method=filemetas&access_token=" +
		b.AccessToken + "&fsids=" + sids + "&thumb=" + path + "&thumb=0&dlink=1&extra=0&needmedia=0&detail=0"
	list, err := HttpGets(urls)
	if err != nil {
		return "", err
	}
	return list, nil
}

// GetToken 获取token
func (b *Bcloud) GetToken() (int64, error) {
	url := BaiDuOpenApi + "oauth/2.0/token?grant_type=device_token&code=" +
		b.DeviceCode + "&client_id=" + b.AppKey + "&client_secret=" + b.AppSecret
	token, err := HttpGets(url)
	if err != nil {
		return 9001, err
	}
	if gjson.Get(token, "error").Exists() && gjson.Get(token, "error").String() != "" {
		return 9002, fmt.Errorf("token请求错误:%s", gjson.Get(token, "error").String())
	}
	fresh := gjson.Get(token, "refresh_token").String()
	tokens := gjson.Get(token, "access_token").String()
	b.AccessToken = tokens
	b.RefreshTokens = fresh
	return 0, nil
}

// RefreshToken 获取token
func (b *Bcloud) RefreshToken() error {
	url := BaiDuOpenApi + "oauth/2.0/token?grant_type=refresh_token&refresh_token=" + b.RefreshTokens +
		"&client_id=" + b.AppKey + "&client_secret=" + b.AppSecret
	token, err := HttpGets(url)
	if err != nil {
		return err
	}
	fresh := gjson.Get(token, "refresh_token").String()
	tokens := gjson.Get(token, "access_token").String()
	b.AccessToken = tokens
	b.RefreshTokens = fresh
	return nil
}

// GetUserInfo 获取用户信息
func (b *Bcloud) GetUserInfo() (string, error) {
	url := BaiDuPanApi + "rest/2.0/xpan/nas?method=uinfo&access_token=" + b.AccessToken
	info, err := HttpGets(url)
	return info, err
}

// GetCategoryTotal 获取当前目录下的文件数量
func (b *Bcloud) GetCategoryTotal(name string, category int) (string, error) {
	url := BaiDuPanApi + "api/categoryinfo?parent_path=" + name + "&access_token=" + b.AccessToken +
		"&category=" + strconv.Itoa(category) + "&recursion=0"
	info, err := HttpGets(url)
	return info, err
}

// GetCategoryList 获取当前目录下的文件列表
func (b *Bcloud) GetCategoryList(name string, category, recursion int) (string, error) {
	url := BaiDuPanApi + "rest/2.0/xpan/multimedia?access_token=" +
		b.AccessToken + "&category=" + strconv.Itoa(category) + "&recursion=" +
		strconv.Itoa(recursion) + "&method=categorylist&parent_path=" + name + "&order=time&desc=1"
	info, err := HttpGets(url)
	return info, err
}

// PianUpload 分片上传文件
func (b *Bcloud) PianUpload(id string) error {
	for i, v := range b.FileList {
		url := BaiDuPcsApi + "rest/2.0/pcs/superfile2?access_token=" + b.AccessToken +
			"&method=upload&type=tmpfile&path=/apps/" + b.AppName + "/" + b.FileName +
			"&uploadid=" + id + "&partseq=" + strconv.Itoa(i)
		info, err := HttpFile(url, b.FileName, v)
		if err != nil {
			return nil
		}
		if errno := gjson.Get(info, "errno").Int(); errno != 0 {
			return fmt.Errorf("分片上传错误%s", gjson.Get(info, "error_msg").String())
		}
	}
	return nil
}

// Create 创建文件
func (b *Bcloud) Create(id string) (string, error) {
	urls := BaiDuPanApi + "rest/2.0/xpan/file?method=create&access_token=" + b.AccessToken
	blockList, err := json.Marshal(b.Block)
	if err != nil {
		return "", err
	}
	params := map[string]string{
		"path":       "/apps/" + b.AppName + "/" + b.FileName,
		"uploadid":   id,
		"size":       strconv.Itoa(len(b.FileStream)),
		"rtype":      "1",
		"isdir":      "0",
		"block_list": string(blockList),
	}
	list, err := HttpPost(urls, params)
	if err != nil {
		return "", nil
	}
	if errno := gjson.Get(list, "errno").Int(); errno != 0 {
		return "", fmt.Errorf("分片创建文件错误%s", gjson.Get(list, "error_msg").String())
	}
	return list, nil
}

// PreUpload 预上传文件
func (b *Bcloud) PreUpload() (string, error) {
	urls := BaiDuPanApi + "rest/2.0/xpan/file?method=precreate&access_token=" + b.AccessToken
	block := []string{}
	for _, v := range splitBytes(b.FileStream, blockMaxSize) {
		b.FileList = append(b.FileList, bytes.NewReader(v))
		block = append(block, encrypt.Md5ByteString(v))
	}
	b.Block = block
	blockList, err := json.Marshal(block)
	if err != nil {
		return "", err
	}
	params := map[string]string{
		"path":       "/apps/" + b.AppName + "/" + b.FileName,
		"size":       strconv.Itoa(len(b.FileStream)),
		"rtype":      "1",
		"isdir":      "0",
		"autoinit":   "1",
		"block_list": string(blockList),
	}
	list, err := HttpPost(urls, params)
	return list, err

}
